package com.spring.arch.common.utils;


import com.spring.arch.common.exception.AppRuntimeException;
import org.springframework.util.Base64Utils;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;

public class CryptoForJwt {
    private final String IV = "2019070500241234";
    private final String ENGINE = "AES";
    private final String CRYPTO = "AES/CBC/PKCS5Padding";

    private final IvParameterSpec ivParameter = new IvParameterSpec(this.IV.getBytes());
    private final SecretKeySpec secretKey;


    public CryptoForJwt(final String key) {
        try {
            byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8);
            if (keyBytes.length > 16) {
                keyBytes = Arrays.copyOf(keyBytes, 16);
            }
            this.secretKey = new SecretKeySpec(keyBytes, this.ENGINE);
        } catch (final Exception e) {
            throw new AppRuntimeException("System failed by UTF-8.", e);
        }
    }


    public String encrypt(final String source) {
        try {
            final byte[] data = this.execute(Cipher.ENCRYPT_MODE, source.getBytes(StandardCharsets.UTF_8));
            return Base64Utils.encodeToUrlSafeString(data);
        } catch (final Exception e) {
            throw new AppRuntimeException("System failed when encrypting.", e);
        }
    }

    public String decrypt(final String encrypted) {
        try {
            final byte[] encryptedBytes = Base64Utils.decodeFromUrlSafeString(encrypted);

            final byte[] data = this.execute(Cipher.DECRYPT_MODE, encryptedBytes);
            return new String(data, StandardCharsets.UTF_8);
        } catch (final Exception e) {
            throw new AppRuntimeException("System failed when decrypting.", e);
        }
    }

    private byte[] execute(final int mode, final byte[] data)
            throws NoSuchPaddingException, NoSuchAlgorithmException,
            InvalidAlgorithmParameterException,
            InvalidKeyException,
            BadPaddingException, IllegalBlockSizeException {

        final Cipher cipher = Cipher.getInstance(this.CRYPTO);
        cipher.init(mode, this.secretKey, this.ivParameter);

        return cipher.doFinal(data);
    }

}